home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / Icon 8.1 / msm-2 / iconc.sit / tlex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-19  |  12.7 KB  |  526 lines  |  [TEXT/MPS ]

  1. /*
  2.  * tlex.c -- the lexical analyzer.
  3.  */
  4.  
  5. #include "::h:gsupport.h"
  6. #include "trans.h"
  7. #include "token.h"
  8. #include "tlex.h"
  9. #include "tree.h"
  10. #include "tcode.h"
  11. #include "tsym.h"
  12. #include "tproto.h"
  13. #include <ctype.h>
  14.  
  15. /*
  16.  * Prototypes.
  17.  */
  18.  
  19. hidden int              bufcmp          Params((char *s));
  20. hidden struct toktab   *findres        Params((noargs));
  21. hidden struct toktab   *getident    Params((int ac,int *cc));
  22. hidden struct toktab   *getnum        Params((int ac,int *cc));
  23. hidden struct toktab   *getstring    Params((int ac,int *cc));
  24. hidden    int        setfilenm    Params((int c));
  25. hidden    int        setlineno    Params((noargs));
  26.  
  27. #define isletter(s)    (isupper(c) | islower(c))
  28.  
  29. struct node tok_loc =
  30.    {0, NULL, 0, 0};    /* "model" node containing location of current token */
  31.  
  32. struct str_buf lex_sbuf; /* string buffer for lexical analyzer */
  33.  
  34. /*
  35.  * yylex - find the next token in the input stream, and return its token
  36.  *  type and value to the parser.
  37.  *
  38.  * Variables of interest:
  39.  *
  40.  *  cc - character following last token.
  41.  *  nlflag - set if a newline was between the last token and the current token
  42.  *  lastend - set if the last token was an Ender.
  43.  *  lastval - when a semicolon is inserted and returned, lastval gets the
  44.  *   token value that would have been returned if the semicolon hadn't
  45.  *   been inserted.
  46.  */
  47.  
  48. static struct toktab *lasttok = NULL;
  49. static int lastend = 0;
  50. static int eofflag = 0;
  51. static int cc = '\n';
  52.  
  53. int yylex()
  54.    {
  55.    register struct toktab *t;
  56.    register int c;
  57.    int n;
  58.    int nlflag;
  59.    static nodeptr lastval;
  60.    static struct node semi_loc;
  61.  
  62.    if (lasttok != NULL) {
  63.       /*
  64.        * A semicolon was inserted and returned on the last call to yylex,
  65.        *  instead of going to the input, return lasttok and set the
  66.        *  appropriate variables.
  67.        */
  68.  
  69.       yylval = lastval;
  70.       tok_loc = *lastval;
  71.       t = lasttok;
  72.       goto ret;
  73.       }
  74.    nlflag = 0;
  75. loop:
  76.    c = cc;
  77.    /*
  78.     * Remember where a semicolon will go if we insert one.
  79.     */
  80.    semi_loc.n_file = tok_loc.n_file;
  81.    semi_loc.n_line = in_line;
  82.    semi_loc.n_col = incol;
  83.    /*
  84.     * Skip whitespace and comments and process #line directives.
  85.     */
  86.    while (c == Comment || isspace(c)) {
  87.       if (c == '\n') {
  88.          nlflag++;
  89.          c = NextChar;
  90.      if (c == Comment) {
  91.             /*
  92.          * Check for #line directive at start of line.
  93.              */
  94.             if (('l' == (c = NextChar)) &&
  95.                 ('i' == (c = NextChar)) &&
  96.                 ('n' == (c = NextChar)) &&
  97.                 ('e' == (c = NextChar))) {
  98.                c = setlineno();
  99.            while ((c == ' ') || (c == '\t'))
  100.           c = NextChar;
  101.                if (c != EOF && c != '\n')
  102.                   c = setfilenm(c);
  103.            }
  104.         while (c != EOF && c != '\n')
  105.                c = NextChar;
  106.         }
  107.          }
  108.       else {
  109.      if (c == Comment) {
  110.         while (c != EOF && c != '\n')
  111.                c = NextChar;
  112.         }
  113.          else {
  114.             c = NextChar;
  115.             }
  116.          }
  117.       }
  118.    /*
  119.     * A token is the next thing in the input.  Set token location to
  120.     *  the current line and column.
  121.     */
  122.    tok_loc.n_line = in_line;
  123.    tok_loc.n_col = incol;
  124.  
  125.    if (c == EOF) {
  126.       /*
  127.        * End of file has been reached.    Set eofflag, return T_Eof, and
  128.        *  set cc to EOF so that any subsequent scans also return T_Eof.
  129.        */
  130.       if (eofflag++) {
  131.      eofflag = 0;
  132.      cc = '\n';
  133.      yylval = NULL;
  134.      return 0;
  135.      }
  136.       cc = EOF;
  137.       t = T_Eof;
  138.       yylval = NULL;
  139.       goto ret;
  140.       }
  141.  
  142.    /*
  143.     * Look at current input character to determine what class of token
  144.     *  is next and take the appropriate action.  Note that the various
  145.     *  token gathering routines write a value into cc.
  146.     */
  147.    if (isalpha(c) || (c == '_')) {   /* gather ident or reserved word */
  148.       if ((t = getident(c, &cc)) == NULL)
  149.      goto loop;
  150.       }
  151.    else if (isdigit(c) || (c == '.')) {    /* gather numeric literal or "." */
  152.       if ((t = getnum(c, &cc)) == NULL)
  153.      goto loop;
  154.       }
  155.    else if (c == '"' || c == '\'') {    /* gather string or cset literal */
  156.       if ((t = getstring(c, &cc)) == NULL)
  157.      goto loop;
  158.       }
  159.    else {            /* gather longest legal operator */
  160.       if ((n = getopr(c, &cc)) == -1)
  161.      goto loop;
  162.       t = &(optab[n].tok);
  163.       yylval = OpNode(n);
  164.       }
  165.    if (nlflag && lastend && (t->t_flags & Beginner)) {
  166.       /*
  167.        * A newline was encountered between the current token and the last,
  168.        *  the last token was an Ender, and the current token is a Beginner.
  169.        *  Return a semicolon and save the current token in lastval.
  170.        */
  171.       lastval = yylval;
  172.       lasttok = t;
  173.       tok_loc = semi_loc;
  174.       yylval = OpNode(semicol_loc);
  175.       return SEMICOL;
  176.       }
  177. ret:
  178.    /*
  179.     * Clear lasttok, set lastend if the token being returned is an
  180.     *  Ender, and return the token.
  181.     */
  182.    lasttok = 0;
  183.    lastend = t->t_flags & Ender;
  184.    return (t->t_type);
  185.    }
  186.  
  187. /*
  188.  * getident - gather an identifier beginning with ac.  The character
  189.  *  following identifier goes in cc.
  190.  */
  191.  
  192. static struct toktab *getident(ac, cc)
  193. int ac;
  194. int *cc;
  195.    {
  196.    register int c;
  197.    register struct toktab *t;
  198.  
  199.    c = ac;
  200.    /*
  201.     * Copy characters into string space until a non-alphanumeric character
  202.     *  is found.
  203.     */
  204.    do {
  205.       AppChar(lex_sbuf, c);
  206.       c = NextChar;
  207.       } while (isalnum(c) || (c == '_'));
  208.    *cc = c;
  209.    /*
  210.     * If the identifier is a reserved word, make a ResNode for it and return
  211.     *  the token value.  Otherwise, install it with putid, make an
  212.     *  IdNode for it, and return.
  213.     */
  214.    if ((t = findres()) != NULL) {
  215.       lex_sbuf.endimage = lex_sbuf.strtimage;
  216.       yylval = ResNode(t->t_type);
  217.       return t;
  218.       }
  219.    else {
  220.       yylval = IdNode(str_install(&lex_sbuf));
  221.       return (struct toktab *)T_Ident;
  222.       }
  223.    }
  224.  
  225. /*
  226.  * findres - if the string just copied into the string space by getident
  227.  *  is a reserved word, return a pointer to its entry in the token table.
  228.  *  Return NULL if the string isn't a reserved word.
  229.  */
  230.  
  231. static struct toktab *findres()
  232.    {
  233.    register struct toktab *t;
  234.    register char c;
  235.  
  236.    c = *lex_sbuf.strtimage;
  237.    if (!islower(c))
  238.       return NULL;
  239.    /*
  240.     * Point t at first reserved word that starts with c (if any).
  241.     */
  242.    if ((t = restab[c - 'a']) == NULL)
  243.       return NULL;
  244.    /*
  245.     * Search through reserved words, stopping when a match is found
  246.     *  or when the current reserved word doesn't start with c.
  247.     */
  248.    while (t->t_word[0] == c) {
  249.       if (bufcmp(t->t_word))
  250.      return t;
  251.       t++;
  252.       }
  253.    return NULL;
  254.    }
  255.  
  256. /*
  257.  * bufcmp - compare a null terminated string to what is in the string buffer.
  258.  */
  259. static int bufcmp(s)
  260. char *s;
  261.    {
  262.    register char *s1;
  263.    s1 = lex_sbuf.strtimage;
  264.    while (s != '\0' && s1 < lex_sbuf.endimage && *s == *s1) {
  265.       ++s;
  266.       ++s1;
  267.       }
  268.    if (*s == '\0' && s1 == lex_sbuf.endimage)
  269.       return 1;
  270.    else
  271.       return 0;
  272.    }
  273.  
  274. /*
  275.  * getnum - gather a numeric literal starting with ac and put the
  276.  *  character following the literal into *cc.
  277.  *
  278.  * getnum also handles the "." operator, which is distinguished from
  279.  *  a numeric literal by what follows it.
  280.  */
  281.  
  282. static struct toktab *getnum(ac, cc)
  283. int ac;
  284. int *cc;
  285.    {
  286.    register int c, r, state;
  287.    int realflag, n, dummy;
  288.  
  289.    c = ac;
  290.    if (c == '.') {
  291.       state = 7;
  292.       }
  293.    else {
  294.       r = tonum(c);
  295.       state = 0;
  296.       realflag = 0;
  297.       }
  298.    for (;;) {
  299.       AppChar(lex_sbuf, c);
  300.       c = NextChar;
  301.       switch (state) {
  302.      case 0:        /* integer part */
  303.         if (isdigit(c))        { r = r * 10 + tonum(c); continue; }
  304.         if (c == '.')           { state = 1; realflag++; continue; }
  305.         if (c == 'e' || c == 'E')  { state = 2; realflag++; continue; }
  306.         if (c == 'r' || c == 'R')  {
  307.            state = 5;
  308.            if (r < 2 || r > 36)
  309.           tfatal("invalid radix for integer literal", (char *)NULL);
  310.            continue;
  311.            }
  312.         break;
  313.      case 1:        /* fractional part */
  314.         if (isdigit(c))   continue;
  315.         if (c == 'e' || c == 'E')   { state = 2; continue; }
  316.         break;
  317.      case 2:        /* optional exponent sign */
  318.         if (c == '+' || c == '-') { state = 3; continue; }
  319.      case 3:        /* first digit after e, e+, or e- */
  320.         if (isdigit(c)) { state = 4; continue; }
  321.         tfatal("invalid real literal", (char *)NULL);
  322.         break;
  323.      case 4:        /* remaining digits after e */
  324.         if (isdigit(c))   continue;
  325.         break;
  326.      case 5:        /* first digit after r */
  327.         if ((isdigit(c) || isletter(c)) && tonum(c) < r)
  328.            { state = 6; continue; }
  329.         tfatal("invalid integer literal", (char *)NULL);
  330.         break;
  331.      case 6:        /* remaining digits after r */
  332.         if (isdigit(c) || isletter(c)) {
  333.            if (tonum(c) >= r) {    /* illegal digit for radix r */
  334.           tfatal("invalid digit in integer literal", (char *)NULL);
  335.           r = tonum('z');       /* prevent more messages */
  336.           }
  337.            continue;
  338.            }
  339.         break;
  340.          case 7:        /* token began with "." */
  341.             if (isdigit(c)) {
  342.                state = 1;        /* followed by digit is a real const */
  343.                realflag = 1;
  344.                continue;
  345.                }
  346.             *cc = c;            /* anything else is just a dot */
  347.             lex_sbuf.endimage--;    /* remove dot (undo AppChar) */
  348.             n = getopr((int)'.', &dummy);
  349.             yylval = OpNode(n);
  350.             return &(optab[n].tok);
  351.          }
  352.       break;
  353.       }
  354.    *cc = c;
  355.    if (realflag) {
  356.       yylval = RealNode(str_install(&lex_sbuf));
  357.       return T_Real;
  358.       }
  359.    yylval = IntNode(str_install(&lex_sbuf));
  360.    return T_Int;
  361.    }
  362.  
  363. /*
  364.  * getstring - gather a string literal starting with ac and place the
  365.  *  character following the literal in *cc.
  366.  */
  367. static struct toktab *getstring(ac, cc)
  368. int ac;
  369. int *cc;
  370.    {
  371.    register int c, sc;
  372.    int sav_indx;
  373.    int len;
  374.  
  375.    sc = ac;
  376.    sav_indx = -1;
  377.    c = NextChar;
  378.    while (c != sc && c != '\n' && c != EOF) {
  379.       /*
  380.        * If a '_' is the last non-white space before a new-line,
  381.        *  we must remember where it is.
  382.        */
  383.       if (c == '_')
  384.          sav_indx = lex_sbuf.endimage - lex_sbuf.strtimage;
  385.       else if (!isspace(c))
  386.          sav_indx = -1;
  387.  
  388.       if (c == Escape) {
  389.          c = NextChar;
  390.          if (c == EOF)
  391.             break;
  392.          AppChar(lex_sbuf, Escape);
  393.          if (c == '^') {
  394.             c = NextChar;
  395.             if (c == EOF)
  396.                break;
  397.             AppChar(lex_sbuf, '^');
  398.             }
  399.      }
  400.       AppChar(lex_sbuf, c);
  401.       c = NextChar;
  402.  
  403.       /*
  404.        * If a '_' is the last non-white space before a new-line, the
  405.        *  string continues at the first non-white space on the next line
  406.        *  and everything from the '_' to the end of this line is ignored.
  407.        */
  408.       if (c == '\n' && sav_indx >= 0) {
  409.          lex_sbuf.endimage = lex_sbuf.strtimage + sav_indx;
  410.          while ((c = NextChar) != EOF && isspace(c))
  411.             ;
  412.          }
  413.       }
  414.    if (c == sc)
  415.       *cc = ' ';
  416.    else {
  417.       tfatal("unclosed quote", (char *)NULL);
  418.       *cc = c;
  419.       }
  420.    len = lex_sbuf.endimage - lex_sbuf.strtimage;
  421.    if (ac == '"') {     /* a string literal */
  422.       yylval = StrNode(str_install(&lex_sbuf), len);
  423.       return T_String;
  424.       }
  425.    else {        /* a cset literal */
  426.       yylval = CsetNode(str_install(&lex_sbuf), len);
  427.       return T_Cset;
  428.       }
  429.    }
  430.  
  431.  
  432. /*
  433.  * setlineno - set line number from #line comment, return following char.
  434.  */
  435.  
  436. static int setlineno()
  437.    {
  438.    register int c;
  439.  
  440.    while ((c = NextChar) == ' ' || c == '\t')
  441.       ;
  442.    if (c < '0' || c > '9') {
  443.       tfatal("no line number in #line directive", "");
  444.       while (c != EOF && c != '\n')
  445.      c = NextChar;
  446.       return c;
  447.       }
  448.    in_line = 0;
  449.    while (c >= '0' && c <= '9') {
  450.       in_line = in_line * 10 + (c - '0');
  451.       c = NextChar;
  452.       }
  453.    return c;
  454.    }
  455.  
  456. /*
  457.  * setfilenm -    set file name from #line comment, return following char.
  458.  */
  459.  
  460. static int setfilenm(c)
  461. register int c;
  462.    {
  463.    while (c == ' ' || c == '\t')
  464.       c = NextChar;
  465.    if (c != '"') {
  466.       tfatal("'\"' missing from file name in #line directive", "");
  467.       while (c != EOF && c != '\n')
  468.      c = NextChar;
  469.       return c;
  470.       }
  471.    while ((c = NextChar) != '"' && c != EOF && c != '\n')
  472.       AppChar(lex_sbuf, c);
  473.    if (c == '"') {
  474.       tok_loc.n_file = str_install(&lex_sbuf);
  475.       return NextChar;
  476.       }
  477.    else {
  478.       tfatal("'\"' missing from file name in #line directive", "");
  479.       return c;
  480.       }
  481.    }
  482.  
  483. /*
  484.  * nextchar - return the next character in the input.
  485.  */
  486.  
  487. int nextchar()
  488.    {
  489.    register int c;
  490.  
  491.    if (c = peekc) {
  492.       peekc = 0;
  493.       return c;
  494.       }
  495.    c = getc(srcfile);
  496.    switch (c) {
  497.       case EOF:
  498.      if (incol) {
  499.         c = '\n';
  500.         in_line++;
  501.         incol = 0;
  502.         peekc = EOF;
  503.         break;
  504.         }
  505.      else {
  506.         in_line = 0;
  507.         incol = 0;
  508.         break;
  509.         }
  510.       case '\n':
  511.      in_line++;
  512.      incol = 0;
  513.      break;
  514.       case '\t':
  515.      incol = (incol | 7) + 1;
  516.      break;
  517.       case '\b':
  518.      if (incol)
  519.         incol--;
  520.      break;
  521.       default:
  522.      incol++;
  523.       }
  524.    return c;
  525.    }
  526.